""" Loading functions for Worlds. """

import os
import pygame
from .. import debug, resource
from ..error import FormatError
from ..misc import strbool
from ..tile import TileMap
from ..world import World
from ..entity import create as create_entity
from .util import get_elem_value
import tileset, image
from xml.etree import ElementTree as ET

__all__ = (
    'load'
)

VERSION = '0.03'

def load(fileOrPath,relPath=''):
    """ Loads a TileMap object from a tilemap resource file (.rtp). """
    # Use relPath if needed, ignore it if not
    try:
        relPath,tail = os.path.split(fileOrPath)
    except (AttributeError,TypeError):
        pass
    
    # Start parsing
    tree = ET.parse(fileOrPath)
    root = tree.getroot()
    
    # Verify the version
    vers = root.get('version').strip()
    if vers != VERSION:
        raise FormatError("Bad version number '%s'"%vers)
    
    # Get the easy stuff
    wldID = root.get('id',None)
    wx = get_elem_value(root.find('x'), int, 0)
    wy = get_elem_value(root.find('y'), int, 0)
    ww = get_elem_value(root.find('w'), int, 0)
    wh = get_elem_value(root.find('h'), int, 0)
    
    # Get the background color
    bgcolorElem = root.find('bgcolor')
    if bgcolorElem is not None:
        color = [int(v) for v in bgcolorElem.text.split(',')]
    else:
        color = None
    
    # Create the world
    wld = World(wldID,(wx,wy,ww,wh))
    
    # Set the background color
    wld.bgcolor = color
    
    # Now parse through the huge gilgamesh of entities...
    for eElem in (root.find('entities') or []):
        tag = eElem.tag
        
        # Entity object
        if tag == 'entity':
            e = _parse_entity(eElem,wld,relPath)
    
    # And the tilemaps...
    for eElem in (root.find('tilemaps') or []):
        tag = eElem.tag
        
        # Entity object
        if tag == 'tilemap':
            e = _parse_tilemap(eElem,wld,relPath)
    
    return wld

def _autoload(agent):
    """ Internal function.
        Autoloader for worlds.
    """
    return load(agent.src)

def _parse_entity(root,wld,srcPre=''):
    """ Internal function.
    
        Used to parse the basic entity XML data into a new entity.
        Returns a new entity of the type specified in the XML.
        
        root:   Pass the entity 'root element' (an ElementTree.Element-compatible object)
        wld:    A world to place the new entity into.
        srcPre: Pass a file path to prepend to file paths given in the XML, such
                as the `src` attribute of the `sprite` tag.
                
        If the entity created has a parse_xml method it will be called. The
        method will be passed the entity XML as a string and the srcPre string.
        
        TODO: Allow for more than just images to be loaded from XML.
    """
    # Get the ID
    entID = root.get('id',None)
    
    # Get the type
    typeID = root.get('type',None)
    
    # Fail if the tag is wrong
    if root.tag != 'entity':
        raise FormatError("Entity %sXML tag is not 'entity'."%("'%s' "%entID if entID else ''))
    
    # Get the position
    elem = root.find('pos')
    if elem is not None:
        pos = [get_elem_value(elem.find(v), float, 0.0) for v in ('x','y')]
    else:
        pos = None
    
    # Get the box
    elem = root.find('box')
    if elem is not None:
        box = [get_elem_value(elem.find(v), float, 0.0) for v in ('x','y','w','h')]
    else:
        box = None
    
    # Get the layer 
    layer = get_elem_value(root.find('layer'), int, 0)
    
    # Get the sprite
    elem = root.find('sprite')
    if elem is not None:
        ref = elem.get('ref',None)
        src = elem.get('src',None)
        if ref:
            sprite = resource.get(ref)
        elif src:
            sprite = image.load_image(src, strbool(elem.get('convert')),
                                           srtbool(elem.get('alpha')))
        else:
            sprite = None
    else:
        sprite = None
        
    # Create the entity
    ent = create_entity(typeID,entID,world=wld,pos=pos,box=box,sprite=sprite,layer=layer)
    
    # Call the entity's XML parser if present
    if hasattr(ent,'parse_xml'):
        ent.parse_xml(ET.tostring(root),srcPre)
        
    return ent

def _parse_tilemap(root,wld,srcPre=''):
    """ Internal function.
        Same as _parse_entity, but parses a `tilemap` element instead.
    """
    
    # Get ID of tilemap
    tmapID = root.get('id',None)
    
    # Get the tileset data element
    elem = root.find('tileset')
    if elem is None:
        raise FormatError("Encountered tilemap with no tileset.")
    
    # Try to load the tileset data
    ref = elem.get('ref',None)
    src = elem.get('src',None)
    if ref:
        tset = resource.depend(ref,'tileset')
    elif src:
        tset = tileset.load(src)
    else:
        raise FormatError("Encountered tilemap with invalid tilset.")
    
    # Get position and dimension data
    
    # Get the position
    elem = root.find('pos')
    if elem is not None:
        pos = [get_elem_value(elem.find(v), float, 0.0) for v in ('x','y')]
    else:
        pos = None
        
    # Get the size of cells
    elem = root.find('cellsize')
    if elem is not None:
        cellSize = [get_elem_value(elem.find(v), int, 0) for v in ('w','h')]
    else:
        cellSize = None

    # Get the size of grid
    elem = root.find('size')
    if elem is not None:
        size = [get_elem_value(elem.find(v), int, 0) for v in ('w','h')]
    else:
        size = None
    
    # Get the layer 
    layer = get_elem_value(root.find('layer'), int, 0)
    
    # Get tile data
    elem = root.find('tdata')
    if elem is None:
        raise FormatError("Enountered tilemap with no tile data.")
    # Get data format
    tdFormat = elem.get('format','dec')
    # TODO: Allow for other formats besides decimal
    if tdFormat != 'dec':
        raise FormatError("Tilemap data format unknown: %s"%tdFormat)
    tiles = [int(v) for v in elem.text.split(',')]
    
    # Create the tilemap
    tmap = TileMap(tmapID,tset,cellSize,size,tiles,pos,layer)
    # Add it to the world
    wld.add_tilemap(tmap)

    return tmap
